package uk.co.q3c.deplan.server.dao;

import java.util.ArrayList;
import java.util.Date;

import org.apache.log4j.Logger;

import uk.co.q3c.deplan.client.domain.AbstractDomainObject;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.resource.ResourceGroup;
import uk.co.q3c.deplan.client.domain.resource.ResourceManager;
import uk.co.q3c.deplan.client.domain.resource.ResourcePool;
import uk.co.q3c.deplan.client.service.PlanManager;
import uk.co.q3c.deplan.client.service.ServiceFactory;
import uk.co.q3c.deplan.server.Server;
import uk.co.q3c.deplan.server.rcp.model.ModelChangeListener;
import uk.co.q3c.deplan.server.rcp.model.TaskManager;
import uk.co.q3c.deplan.server.rcp.model.UndoRestoreList;
import uk.co.q3c.deplan.server.service.Leveller;
import uk.co.q3c.deplan.server.util.ProgressListener;

/**
 * A static class used to access the domain model, effectively the outer access point as a data access layer.
 * 
 * @author DSowerby 19 Jun 2009
 * 
 */
public class Model {
	static transient protected final Logger logger = Logger.getLogger(Model.class.getName());
	private static TaskManager taskManager;
	private static ResourceManager resourceManager;
	private static ArrayList<ModelChangeListener> listeners;
	static Date levelDate;
	static ServiceFactory connection;
	static PlanManager planManager;
	private static UndoRestoreList undoRestoreList;

	public static TaskManager taskManager() {
		return taskManager;
	}

	public static ResourceManager resourceManager() {
		return resourceManager;
	}

	public static void addChangeListener(ModelChangeListener modelChangeListener) {
		listeners.add(modelChangeListener);
	}

	public static void level(ProgressListener progressListener) throws PersistenceReferenceException {
		Logger.getLogger(Model.class.getName())
				.info("start levelling");
		Leveller leveller = new Leveller(connection);
		leveller.setProgressListener(progressListener);
		fireProgressStep(progressListener);
		leveller.addCandidate(Model.taskManager()
				.getRoot());
		fireProgressStep(progressListener);

		if (levelDate == null) {
			levelDate = new Date();
		}

		leveller.setLevelDate(levelDate);// today
		leveller.setResourcePool(resourceManager.getResourcePool());
		Logger.getLogger(Model.class.getName())
				.info("Levelling date set to " + leveller.getLevelDate());
		fireProgressStep(progressListener);
		leveller.level();

		fireLevellingEnd();
		Logger.getLogger(Model.class.getName())
				.info("plannedFinish levelling");
	}

	private static void fireLevellingEnd() {
		for (ModelChangeListener listener : listeners) {
			listener.levellingComplete();
		}
	}

	private static void fireProgressStep(ProgressListener progressListener) {
		if (progressListener != null) {
			progressListener.incStep();
		}
	}

	public static Date getLevelDate() {
		return levelDate;
	}

	public static void level(ProgressListener levellingProgressListener, Date levelDate) throws PersistenceReferenceException {
		Model.levelDate = levelDate;
		Model.level(levellingProgressListener);
	}

	public static ResourcePool getResourcePool() {
		return getConnection().resourceService()
				.findResourcePools()
				.get(0);
	}

	public static ResourceGroup[] resourceGroups() {
		return resourceManager().getResourcePool()
				.groupsArray();
	}

	/**
	 * Sets the dao then refreshes connection related references
	 * 
	 * @param dao
	 */
	public static void setConnection(ServiceFactory connection) {
		Model.connection = connection;
		// need open dao for new resource manager
		// if (dao.isClosed()) {
		// dao.open();
		// }
		// listeners = new ArrayList<ModelChangeListener>();
		// resourceManager = new ResourceManager();
		// taskManager = new TaskManager();
		// getPlanManager();
	}

	/**
	 * Connects to the database specified in preferences. If a connection is already open, the call is ignored - use {@link #disconnect()}
	 * first if you want to connect to a different source
	 * 
	 * @param shell
	 */
	// public static void connectToDatabase(Shell shell) {
	// set up database - but will it acknowledge user preferences?
	// if ((dao == null) || (dao.isClosed())) {
	// String s =
	// DePlanRCPPlugin.getDefault().getPreferenceStore().getString(PreferenceConstants.PRODUCTIONDB);
	// try {
	// OpenAppDialog openAppDialog = new OpenAppDialog(shell);
	// openAppDialog.setPath(s);
	// openAppDialog.open();
	// openAppDialog.appendText("Loading database");
	// openAppDialog.close();
	//
	// } catch (Exception e) {
	// Logger.getLogger(Model.class.getName()).warninge.getMessage());
	// }
	// }
	// }

	// public static void disconnect() {
	// dao.getDbc().close();
	// }

	/**
	 * Loads a planManager from the database, or if there isn't one, a new one is created within the dao.
	 * 
	 * @return
	 */
	public static PlanManager getPlanManager() {
		// if (dao == null) {
		// return new PlanManager(dao);
		// }
		// if (dao.isClosed()) {
		// return new PlanManager(dao);
		// }
		// return connection.domainService().loadPlanManager();
		throw new RuntimeException("not yet implemented");
	}

	/**
	 * When a persistent object is activated, and therefore may be changed, this method is called to allow the Model to listen for any
	 * property changes which may occur. At the moment this facility is used only by the {@link UndoRestoreList}
	 * 
	 * @param abstractModelObject
	 */
	public static void registerPersistentObject(AbstractDomainObject abstractModelObject) {
		if (undoRestoreList == null) {
			undoRestoreList = new UndoRestoreList();
		}
		abstractModelObject.addPropertyChangeListener(undoRestoreList);
	}

	/**
	 * Direct reference to {@link UndoRestoreList}
	 * 
	 * @return
	 */
	public static UndoRestoreList undoRestoreList() {
		return undoRestoreList;
	}

	/**
	 * Loads form rules from the database and applies them to the autoEditor
	 * 
	 * @param target
	 */
	// private static void loadRules(AutoEditor autoEditor) {
	// AutoEditorRules rules = null;
	// if (dao == null) {
	// rules = new AutoEditorRules();
	// Logger.getLogger(Model.class.getName()).warning"Rules cannot be loaded, no database specified");
	// } else {
	// if (dao.isClosed()) {
	// dao.open();
	// }
	// rules =
	// dao.getDesignDao().getAutoEditorRules(autoEditor.getTarget().getClass().getName());
	// }
	// autoEditor.setRules(rules);
	// rules.setTarget(autoEditor.getTarget());
	// }
	//
	// public static AutoEditor openEditor(IWorkbenchPage page,
	// AbstractDomainObject target) throws PartInitException {
	// AutoEditor autoEditor = new AutoEditor(target);
	// page.openEditor(target.editorDescriptor(), AutoEditor.ID);
	// loadRules(autoEditor);
	// return autoEditor;
	// }

	// public static void openEditor(IWorkbenchPage workbenchPage,
	// AbstractDomainObject target) throws PartInitException {
	// AutoEditor autoEditor = getEditor(target);
	// IEditorInput input = new AutoEditorInput(target.getName(), target
	// .getName());
	// workbenchPage.openEditor(input, AutoEditor.ID);
	// }

	// /**
	// * Factory method producing content and label providers for lists of
	// * persisted objects. Parameter is used only for type safety
	// *
	// * @return
	// */
	// public static ContentProvider getListPoviderInstance(Class<? extends
	// NamedObject> forClass) {
	// return new ContentProvider();
	// }

	/**
	 * Returns from the database a list of all objects of the same class as the given example, or an empty list if none held or the database
	 * is closed.
	 * 
	 * @param clazz
	 * @return
	 */
	// public static List<? extends DomainObject> getSelectionList(Class<?
	// extends DomainObject> clazz) {
	// List<? extends DomainObject> results = null;
	// if (dbcCheck()) {
	// results = (List<? extends DomainObject>)
	// dao.selectAll(DiscardedState.INCLUDE_DISCARDED, clazz);
	// }
	// if (results == null) {
	// results = new ArrayList<DomainObject>();
	// }
	// return results;
	// }
	// private static boolean dbcCheck() {
	// if (dao == null) {
	// logger.warn("database not connected");
	// return false;
	// }
	// if (dao.getDbc().isClosed()) {
	// logger.warn("database is closed");
	// return false;
	// }
	// return true;
	// }
	public static ServiceFactory getConnection() {
		return Server.connection();
	}

	// public AbstractTaskViewGWT buildView(BaseTask task) {
	// AbstractTaskViewGWT view = new AbstractTaskViewGWT();
	// return (AbstractTaskViewGWT) buildViewFromDomainObject(view, task);
	// }

	// private Object buildViewFromDomainObject(AbstractPersistedObjectView
	// view, DomainObject source) {
	//
	// view.setDiscarded(source.getDiscarded());
	// view.setDiscardedDate(source.getDiscardedDate());
	// // view.setHidden(source.getHidden());
	// // view.setReadOnly(source.getReadOnly());
	// view.setKey(KeyFactory.keyToString(source.getKey()));
	//
	// return view;
	// }

}
